<script setup lang="ts">
import { YiAction } from '@uublue/yimi-element-plus'
import type { ApiConfigFun } from '@/utils/api'
import type {AxiosRequestConfig} from 'axios'
import type { PropType, Ref } from 'vue'
import { ref, watch } from 'vue'
import InherentPermissionDisplay from '../user/inherent-permission-display.vue'

import { matchRoles, matchPermissions, matchPermission } from '@/utils/resource-security'

const props = defineProps({
  api: {
    type: [Object, Function] as PropType<ApiConfigFun | AxiosRequestConfig>,
    required: true
  },
  currentRole: {
    type: Object as PropType<Record<string, any>>,
    required: true
  },
  currentResource: {
    type: Object as PropType<Record<string, any>>,
    required: true
  }
})

// define emits
const emit = defineEmits<{
  (e: 'onSubmitSuccess', res: any, model: Record<string, any>): void
}>()

// on create
let initSteps = 0
const openSteps = ref(0)

const modelValue: Ref<boolean | undefined> = ref()
const notEditableMsg: Ref<string> = ref('')
const directEdit: Ref<boolean> = ref(true)

const checkedRoles: Ref<string[]> = ref([])
const scopeRoles: string[] = props.currentResource.roleRequirement?.codes || []
const checkedPermissions: Ref<string[]> = ref([])
const scopePermissions: string[] = props.currentResource.permissionRequirement?.codes || []
const checkedOwnedPermissions: Ref<string[]> = ref([])
const scopeOwnedPermissions: Ref<string[]> = ref([])

const inherentPermissions: Ref<
  Record<
    string,
    {
      roles: string[]
      permissions: string[]
    }
  >
> = ref({})

function initContext() {
  inherentPermissions.value = {}

  checkedRoles.value = []
  checkedPermissions.value = []

  checkedOwnedPermissions.value = []
  scopeOwnedPermissions.value = []

  if (scopePermissions.length > 0) {
    if (scopeRoles.includes(props.currentRole.code)) {
      checkedRoles.value.push(props.currentRole.code)
    }

    props.currentRole.allPermissions.forEach((ownedPermission: string) => {
      if (scopePermissions.includes(ownedPermission)) {
        checkedPermissions.value.push(ownedPermission)
      } else {
        scopePermissions.forEach((_permission: string) => {
          if (matchPermission(ownedPermission, _permission)) {
            insurePermissionFromPermission(_permission, ownedPermission)
            insureOwnedPermissionChecked(ownedPermission)
          }
        })
      }
    })
  }

  directEdit.value = true
  // 判断可直接编辑性
  if (scopeRoles.length > 0) {
    directEdit.value = false
  } else if (scopeOwnedPermissions.value.length > 0) {
    directEdit.value = false
  } else if (scopePermissions.length > 1) {
    directEdit.value = false
  }

  initSteps++
}

//确保权限来自权限
function insurePermissionFromPermission(permission: string, parentPermission: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (!inherentPermission) {
    inherentPermission = {
      roles: [],
      permissions: []
    }
    inherentPermissions.value[permission] = inherentPermission
  }
  if (!inherentPermission.permissions.includes(parentPermission)) {
    inherentPermission.permissions.push(parentPermission)
  }
}

//确保相关自有权限选中
function insureOwnedPermissionChecked(ownedPermission: string) {
  if (!scopeOwnedPermissions.value.includes(ownedPermission)) {
    scopeOwnedPermissions.value.push(ownedPermission)
  }
  if (!checkedOwnedPermissions.value.includes(ownedPermission)) {
    checkedOwnedPermissions.value.push(ownedPermission)
  }
}

/**
 * 如果该权限在资源权限列表中，
 * 则确保该权限在资源权限列表中被选中
 * @param permission 要选中的资源权限
 */
function insurePermissionChecked(permission: string) {
  if (scopePermissions.includes(permission) && !checkedPermissions.value.includes(permission)) {
    checkedPermissions.value.push(permission)
  }
}

function insurePermissionNotFromPermission(permission: string, parentPermission: string) {
  let inherentPermission = inherentPermissions.value[permission]
  if (inherentPermission) {
    const permissionIndex = inherentPermission.permissions.findIndex(
      (_permission) => _permission == parentPermission
    )
    if (permissionIndex != -1) {
      inherentPermission.permissions.splice(permissionIndex, 1)

      if (inherentPermission.roles.length == 0 && inherentPermission.permissions.length == 0) {
        delete inherentPermissions.value[permission]
      }
    }
  }
}

watch(checkedOwnedPermissions, () => {
  scopeOwnedPermissions.value.forEach((scopeOwnedPermission) => {
    if (checkedOwnedPermissions.value.includes(scopeOwnedPermission)) {
      scopePermissions.forEach((_permission: string) => {
        if (matchPermission(scopeOwnedPermission, _permission)) {
          insurePermissionFromPermission(_permission, scopeOwnedPermission)
          insurePermissionChecked(_permission)
        }
      })
    } else {
      scopePermissions.forEach((_permission: string) => {
        insurePermissionNotFromPermission(_permission, scopeOwnedPermission)
      })
    }
  })
})

//监听用户权限变化
watch(
  () => props.currentRole,
  () => {
    notEditableMsg.value = ''
    const roleMatched = matchRoles([props.currentRole.code], props.currentResource.roleRequirement)
    if (!roleMatched) {
      notEditableMsg.value =
        '需要额外的角色[' +
        props.currentResource.roleRequirement.codes.filter(
          (code: string) => code != props.currentRole.code
        ) +
        ']'
    }

    if (
      !notEditableMsg.value &&
      (!props.currentResource.permissionRequirement ||
        props.currentResource.permissionRequirement.codes.length == 0)
    ) {
      notEditableMsg.value = '没有权限限制'
    }

    const permissionMatchResult = matchPermissions(
      props.currentRole.defaultPermissions,
      props.currentRole.ownedPermissions,
      props.currentResource.permissionRequirement
    )

    if (!notEditableMsg.value && permissionMatchResult && !permissionMatchResult.editable) {
      notEditableMsg.value = '当前角色内置了所需权限'
    }

    modelValue.value = roleMatched && permissionMatchResult.matched

    initContext()
  },
  { immediate: true }
)

function buildRequestData(): Record<string, any> {
  const requestData: Record<string, any> = {
    roleId: props.currentRole.id
  }
  if (directEdit.value) {
    requestData.scopePermissions = scopePermissions

    requestData.checkedPermissions = modelValue.value ? scopePermissions : []
  } else {
    requestData.scopePermissions = scopePermissions
      .concat(scopeOwnedPermissions.value)
      .filter((permission) => {
        return inherentPermissions.value[permission] == undefined
      })

    requestData.checkedPermissions = checkedPermissions.value
      .concat(checkedOwnedPermissions.value)
      .filter((permission) => {
        return inherentPermissions.value[permission] == undefined
      })
  }

  return requestData
}
</script>

<template>
  <el-tooltip v-if="notEditableMsg" :content="notEditableMsg" effect="light" placement="right">
    <template #default>
      <el-switch v-model="modelValue" :disabled="true" />
    </template>
  </el-tooltip>
  <yi-action
    v-else
    v-model="modelValue"
    :api="api"
    :modal-title="'【' + currentResource.name + '】'"
    :on-open="
      ({ resolve }) => {
        openSteps++
        if (openSteps > initSteps) {
          initContext()
        }
        resolve()
      }
    "
    :on-submit="
      (scope, request, close) => {
        request({ data: buildRequestData() }).then(() => {
          close(true)
        })
      }
    "
    trigger="switch"
    model-reserve="never"
    @on-submit-success="
      (res, model) => {
        emit('onSubmitSuccess', res, model)
      }
    "
  >
    <template v-if="!directEdit" #default>
      <el-form class="security-action-form" label-width="150px">
        <el-divider>资源所需</el-divider>
        <el-form-item
          v-if="
            currentResource.roleRequirement &&
            Array.isArray(currentResource.roleRequirement.codes) &&
            currentResource.roleRequirement.codes.length > 0
          "
          :label="
            '所需角色' +
            (currentResource.roleRequirement.logic
              ? '(' + currentResource.roleRequirement.logic + ')'
              : '')
          "
        >
          <el-checkbox-group v-model="checkedRoles">
            <el-checkbox
              v-for="(code, index) in scopeRoles"
              :key="index"
              :value="code"
              :label="code"
              disabled
            ></el-checkbox>
          </el-checkbox-group>
        </el-form-item>

        <el-form-item
          v-if="
            currentResource.permissionRequirement &&
            Array.isArray(currentResource.permissionRequirement.codes)
          "
          :label="
            '所需权限' +
            (currentResource.permissionRequirement.logic
              ? '(' + currentResource.permissionRequirement.logic + ')'
              : '')
          "
        >
          <el-checkbox-group v-model="checkedPermissions">
            <template v-for="(code, index) in scopePermissions" :key="index">
              <el-tooltip
                v-if="inherentPermissions[code] != undefined"
                placement="top"
                effect="light"
              >
                <template #default>
                  <el-checkbox :value="code" :label="code" disabled></el-checkbox>
                </template>

                <template #content>
                  <inherent-permission-display :data="inherentPermissions[code]" />
                </template>
              </el-tooltip>
              <el-checkbox v-else :value="code" :label="code"></el-checkbox>
            </template>
          </el-checkbox-group>
        </el-form-item>

        <el-divider v-if="scopeOwnedPermissions.length > 0">已有相关</el-divider>

        <el-form-item v-if="scopeOwnedPermissions.length > 0" label="已有权限">
          <el-checkbox-group v-model="checkedOwnedPermissions">
            <template v-for="(code, index) in scopeOwnedPermissions" :key="index">
              <el-tooltip
                v-if="inherentPermissions[code] != undefined"
                placement="bottom"
                effect="light"
              >
                <template #default>
                  <el-checkbox :value="code" :label="code" disabled></el-checkbox>
                </template>

                <template #content>
                  <inherent-permission-display :data="inherentPermissions[code]" />
                </template>
              </el-tooltip>
              <el-checkbox v-else :value="code" :label="code"></el-checkbox>
            </template>
          </el-checkbox-group>
        </el-form-item>
      </el-form>
    </template>
  </yi-action>
</template>
